%{

#include <iostream>

extern int  yylex( void );
extern void yyerror( char* errorString );
extern int yylineno;

%}

%token PROGRAM
%token BEG END
%token INTEGER BOOLEAN CHAR
%token PROCEDURE
%token VALUE TYPE
%token NOT RELOP
%token WHILE DO ENDWHILE
%token IF THEN ELSE ENDIF
%token READ WRITE
%token ASSIGNOP
%token INT_LITERAL CHAR_LITERAL BOOL_LITERAL
%token IDENTIFIER

%left '&'
%left NOT
%left '=' '<' '>'
%left '+'
%left '*'

%%

program:
  PROGRAM m2 decls m0 block
  ;

block:
  BEG command_list m0 END
  ;

decls:
  decls m0 decl ';'
  | /* empty */
  ;

decl:
  var_decl
  | type_def
  | proc_decl
  ;

var_decl:
  type ':' id_list
  ;

type:
  INTEGER
  | BOOLEAN
  | CHAR
;
m0:
  /* empty */
  ;
m1:
  /* empty */
  ;
m2:
  /* empty */
  ;

type_def:
  TYPE array_name m0 '=' m1 array_def
  ;

array_name:
  identifier
  ;

array_def:
  '[' limits ']' type
  ;

limits:
  INT_LITERAL ':' INT_LITERAL
  ;

proc_decl:
  proc_header proc_body
  ;

proc_header:
  PROCEDURE m0 proc_name proc_params
  ;

proc_body:
  ':' decls m0 block
  ;

proc_params:
  '(' param_list ')'
  | /* empty */
  ;

param_list:
  param_list ',' param
  | param
  ;
param:
  mode type ':' identifier
  ;

mode:
  VALUE
  ;
proc_name:
  identifier
  ;
command_list:
  command
  | command_list ';' m0 command
  ;
id_list:
  identifier
  | id_list ',' identifier
  ;
command:
  command_assign
  | command_while
  | command_if
  | command_read
  | command_write
  | proc_call
  ;

command_assign:
  variable ASSIGNOP expr
  ;
command_while:
  WHILE m0 expr DO m0 command_list ENDWHILE
  ;

command_if:
  IF expr THEN m0 command_list ENDIF
  | IF expr THEN m0 command_list m1 ELSE m0 command_list ENDIF
  ;
command_read:
  READ variable
  ;
command_write:
  WRITE expr
  ;
variable:
  identifier
  | call_index
  ;
call_index:
  index ')'
  ;
proc_call:
  identifier
  | call_index
  ;
index:
  variable2 '(' expr
  | index ',' expr
  ;
variable2:
  identifier
  ;
expr:
  expr '&' m0 expr
  | NOT expr
  | expr '=' expr
  | expr '<' expr
  | expr '>' expr
  | expr '+' expr
  | expr '*' expr
  | variable
  | literal
  | '(' expr ')'
  ;
literal:
  int_char
  | boolean
  ;
int_char:
  integer
  | CHAR_LITERAL
  ;
integer:
  INT_LITERAL
  ;
boolean:
  BOOL_LITERAL
  ;

identifier:
  IDENTIFIER
  ;

%%

int main( void )
{
  yyparse();
  return 0;
}
